package com.hotel.BL;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.EntityName;
import com.Util;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.CompositeFilterOperator;
import com.google.appengine.api.datastore.Query.Filter;
import com.google.appengine.api.datastore.Query.FilterOperator;
import com.google.appengine.api.datastore.Query.FilterPredicate;

import data.Reservation;
import data.SubReservation;

public class SubReservationLogic extends AbstractBL {
	
	public SubReservationLogic(HttpServletRequest req,HttpServletResponse resp){
		this.req = req;
		this.resp = resp;
	}

	public List<SubReservation> CreateSubReservation(List<SubReservation> subReservation) throws EntityNotFoundException
	{
		List<SubReservation> list = new ArrayList<SubReservation>();
		ReservationLogic resvLogic = new ReservationLogic(req,resp);
		for(SubReservation s : subReservation)
		{
			//Move moveable reservation which exist in room to next avaliable
			List<SubReservation> rlist = resvLogic.GetReservationWithinDateInSpecificRoom(s.getHotelId(),s.getDate(),s.getSpan(),s.getRoom().getId());
			for(SubReservation s1 : rlist)
				resvLogic.AutoMoveRoom(s1,s.getRoom().getId());
			
			Key k = datastore.put(s.getEntity());
			s.setId(k.getId());
			s.setKey(k);
			list.add(s);
		}
		return list;
	}
	
	public List<SubReservation> GetSubReservationFromResvId(long hotelId,long resId)
	{

		Query q = new Query(EntityName.SubReservation);
		
		Filter filterHotel = new FilterPredicate("hotelId",FilterOperator.EQUAL,hotelId);
		Filter filterResId = new FilterPredicate("parentId",FilterOperator.EQUAL,resId);

		Filter filter = CompositeFilterOperator.and(filterHotel, filterResId);
		
		q.setFilter(filter)
		.addSort("date");
		
		return GetList(datastore.prepare(q).asList(FetchOptions.Builder.withLimit(10)));
	}
	
	public List<SubReservation> GetList(List<Entity> entities)
	{
		List<SubReservation> list = new ArrayList<SubReservation>();
		for(Entity e : entities)
		{
			SubReservation s = new SubReservation();
			s.setEntity(e);
			list.add(s);
		}
		return list;
	}
	
	public void DeleteSubReservation(long subReservationId){
		Key key = KeyFactory.createKey(EntityName.SubReservation, subReservationId);
		datastore.delete(key);
	}
	
	public Reservation SplitReservation(long hotelId,long resId,long roomtypeId,long roomId,Date from,Date to)
	{
		try{			
			List<SubReservation> list = GetSubReservationFromResvId(hotelId, resId);
			List<SubReservation> newList = new ArrayList<SubReservation>();
			SubReservation s;
			
			ReservationLogic resvLogic = new ReservationLogic(req,resp);
			Reservation resv = resvLogic.GetReservation(resId);
			
			//check room is avaliable for reservation
			List<SubReservation> subRsv = resvLogic.GetUsedRoomWithinDateWithAssign(hotelId,
					from, Util.daysBetween(from, to),roomtypeId);
			for(SubReservation subResv: subRsv)
			{
				boolean rs = resvLogic.CanBeChangeRoom(subResv);
				if(!rs)
					return null;
			}
			
			if(resv.getDateTo().before(to))
			{
				resv.setSpan(Util.daysBetween(resv.getDate(),to));
			}
			
			if(list.size() == 0)
			{
				s = new SubReservation();
				s.setDate(resv.getDate());
				s.setDateTo(resv.getDateTo());
				s.setRoomTypeString(resv.getRoomTypeString());
				s.setRoomString(resv.getRoomString());
				list.add(s);
			}
			
			boolean remove = false;
			for(int i=0;i<list.size();i++)
			{
				remove = false;
				if(from.equals(list.get(i).getDate()))
				{
					if(to.after(list.get(i).getDate()) && to.before(list.get(i).getDateTo()))
					{
						s = new SubReservation();
						s.setDate(to);
						s.setDateTo(list.get(i).getDateTo());
						s.setRoomTypeString(list.get(i).getRoomTypeString());
						s.setRoomString(list.get(i).getRoomString());
						newList.add(s);
					}				
					remove = true;
				}
				if(!to.equals(list.get(i).getDateTo()) && from.after(list.get(i).getDate()) && from.before(list.get(i).getDateTo()))
				{
					s = new SubReservation();
					s.setDate(list.get(i).getDate());
					s.setDateTo(from);
					s.setRoomTypeString(list.get(i).getRoomTypeString());
					s.setRoomString(list.get(i).getRoomString());
					newList.add(s);
					remove = true;
				}
				if(from.before(list.get(i).getDate()) && to.after(list.get(i).getDateTo()))
				{
					remove = true;
				}
				if(!from.equals(list.get(i).getDate()) && to.after(list.get(i).getDate()) && to.before(list.get(i).getDateTo()))
				{
					s = new SubReservation();
					s.setDate(to);
					s.setDateTo(list.get(i).getDateTo());
					s.setRoomTypeString(list.get(i).getRoomTypeString());
					s.setRoomString(list.get(i).getRoomString());
					newList.add(s);
					remove = true;
				}
				if(to.equals(list.get(i).getDateTo()))
				{
					if(from.after(list.get(i).getDate()) && from.before(list.get(i).getDateTo()))
					{
						s = new SubReservation();
						s.setDate(list.get(i).getDate());
						s.setDateTo(from);
						s.setRoomTypeString(list.get(i).getRoomTypeString());
						s.setRoomString(list.get(i).getRoomString());
						newList.add(s);
					}
					remove = true;
				}
				
				if(!remove)
					newList.add(list.get(i));
			}
			s = new SubReservation();
			s.setDate(from);
			s.setDateTo(to);
			s.setRoomTypeString(Long.toString(roomtypeId));
			s.setRoomString(Long.toString(roomId));
			newList.add(s);
			
			Collections.sort(newList, new Comparator<SubReservation>(){
				  public int compare(SubReservation s1, SubReservation s2) {
				    return (int)(s1.getDate().getTime() - s2.getDate().getTime());
				  }
				});
			
			for(int i=0;i<list.size();i++)
				if(list.get(i).getId() > 0)
					DeleteSubReservation(list.get(i).getId());
			
			for(int i=0;i<newList.size();i++)
			{
				newList.get(i).setHotelId(hotelId);
				newList.get(i).setParentId(resId);
			}
			newList = CreateSubReservation(newList);
			
			resv.setAssign(true);
			
			resv.setSubReservationList(newList);
			resvLogic.UpdateReservation(resv);
			return resv;
		}catch(Exception ex){
			return null;
		}
	}

	public SubReservation GetSubReservation(long id) {
		Key key = KeyFactory.createKey(EntityName.SubReservation, id);
		try {
			Entity lang = datastore.get(key);
			SubReservation c = new SubReservation();
			c.setEntity(lang);
			
			return c;
		} catch (EntityNotFoundException e) {
			return null;
		}
	}
	
	public SubReservation UpdateSubReservation(SubReservation subRsv) throws EntityNotFoundException
	{
		Key key = datastore.put(subRsv.getEntity());
		subRsv.setId(key.getId());
		subRsv.setKey(key);
		return subRsv;
	}
	
	//tee edit
	public SubReservation EditSubReservation(long id, Date dateTo,long span) throws EntityNotFoundException
	{
		SubReservation sub = GetSubReservation(id);
		sub.setDateTo(dateTo);
		sub.setSpan(span);

		Key k = datastore.put(sub.getEntity());
		sub.setId(k.getId());
		sub.setKey(k);

		return sub;
	}
	
	// tee edit
	public List<SubReservation> GetSubReservationForDetele(long parentId,Date dateTo)
	{

		Query q = new Query(EntityName.SubReservation);
		
		Filter filterParentId = new FilterPredicate("parentId",FilterOperator.EQUAL,parentId);
		Filter filterDateTo = new FilterPredicate("dateTo",FilterOperator.GREATER_THAN,dateTo);

		Filter filter = CompositeFilterOperator.and(filterParentId, filterDateTo);
		
		q.setFilter(filter);
		
		return GetList(datastore.prepare(q).asList(FetchOptions.Builder.withDefaults()));
	}
	
	//tee edit
	public SubReservation EditDateAndDateTo(long id, Date dateFrom,Date dateTo) throws EntityNotFoundException
	{
		SubReservation sub = GetSubReservation((long)id);
		sub.setDate(dateFrom);
		sub.setDateTo(dateTo);

		Key k = datastore.put(sub.getEntity());
		sub.setId(k.getId());
		sub.setKey(k);

		return sub;
	}

	public List<SubReservation> EditSubResvListDate(List<SubReservation> subres,Date newFrom,Date newTo) throws EntityNotFoundException
	{
		Date from = subres.get(0).getDate();
		Date to = subres.get(0).getDateTo();
		int from_index = 0;
		int to_index = 0;
		
		for(int i=0;i<subres.size();i++){
			if(subres.get(i).getDate().before(from)){
				from = subres.get(i).getDate();
				from_index = i;
			}
		}
		for(int i=0;i<subres.size();i++){
			if(subres.get(i).getDateTo().after(to)){
				to = subres.get(i).getDateTo();
				to_index = i;
			}
		}
		
		if(newFrom.before(from)){
			//edit
			SubReservation subRsv = subres.get(from_index);
			subRsv.setDate(newFrom);
			subRsv.setSpan(Util.daysBetween(newFrom, subRsv.getDateTo()));
			SubReservation xx  = UpdateSubReservation(subRsv);
			subres.set(from_index, xx);
		}
		if(newTo.after(to)){
			//edit
			SubReservation subRsv = subres.get(to_index);
			subRsv.setDateTo(newTo);
			subRsv.setSpan(Util.daysBetween(subRsv.getDate(), newTo));
			SubReservation xx  = UpdateSubReservation(subRsv);
			subres.set(to_index, xx);
		}
		
		for(int i=0;i<subres.size();i++){
			Date df = subres.get(i).getDate();
			Date dt = subres.get(i).getDateTo();
							
			if(dt.before(newFrom) || dt.equals(newFrom)){
				//delete entity
				DeleteSubReservation(subres.get(i).getId());
				subres.remove(i); i--;
				continue;
			}
			if(df.after(newTo) || df.equals(newTo) ){
				//delete
				DeleteSubReservation(subres.get(i).getId());
				subres.remove(i); i--;
				continue;
			}
			if(newFrom.equals(dt) || newTo.equals(df))
			{
				DeleteSubReservation(subres.get(i).getId());
				subres.remove(i); i--;
				continue;
			}
			if(newFrom.after(df) && newFrom.before(dt)){	
				subres.get(i).setDate(newFrom);
				subres.get(i).setSpan(Util.daysBetween(newFrom, subres.get(i).getDateTo()));
				SubReservation xx  = UpdateSubReservation(subres.get(i));
				subres.set(i, xx);
			}
			if(newTo.after(df) && newTo.before(dt)){
				subres.get(i).setDateTo(newTo);
				subres.get(i).setSpan(Util.daysBetween(subres.get(i).getDate(), newTo));
				SubReservation xx  = UpdateSubReservation(subres.get(i));
				subres.set(i, xx);
			}
		}
		return subres;
	}
	
	public SubReservation GetLastSubRes(List<SubReservation> subres)
	{
		if(subres == null || subres.size() == 0) return null;
		SubReservation last = subres.get(0);
		for(SubReservation s:subres)
			if(s.getDateTo().after(last.getDateTo()))
				last = s;
		return last;
	}
}
